Skip to content

feat(firestore): support serverTimestamps option on snapshot listeners#993

Merged
robingenz merged 2 commits into
mainfrom
feat/issue-985-server-timestamps
Jun 4, 2026
Merged

feat(firestore): support serverTimestamps option on snapshot listeners#993
robingenz merged 2 commits into
mainfrom
feat/issue-985-server-timestamps

Conversation

@robingenz

Copy link
Copy Markdown
Member

Summary

  • Adds a new serverTimestamps?: 'estimate' | 'previous' | 'none' option on SnapshotListenerOptions, applied to addDocumentSnapshotListener, addCollectionSnapshotListener, and addCollectionGroupSnapshotListener on Web, Android, and iOS.
  • Threads the value through to the underlying Firebase SDK so pending server timestamps in unacknowledged writes can be returned as null (default), the client-estimated time, or the previous value.

Test plan

  • Document listener returns null for a pending serverTimestamp() field with default settings, and a Timestamp when serverTimestamps: 'estimate' is set (Web, Android, iOS).
  • Same verification for collection and collection-group listeners.
  • Unknown / unset value still resolves to 'none' (no crash).

Closes #985

Expose Firestore's pending server timestamp behavior ('estimate' |
'previous' | 'none') as an option on addDocumentSnapshotListener,
addCollectionSnapshotListener, and addCollectionGroupSnapshotListener
on Web, Android, and iOS. Defaults to 'none' (current behavior).

Closes #985
Copilot AI review requested due to automatic review settings May 25, 2026 08:59

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds cross-platform support for configuring how pending Firestore serverTimestamp() fields are represented in snapshot listener results, by introducing a serverTimestamps option and threading it through Web/Android/iOS implementations.

Changes:

  • Adds serverTimestamps?: 'estimate' | 'previous' | 'none' to SnapshotListenerOptions.
  • Web: passes serverTimestamps into DocumentSnapshot.data(...) for document/collection/collection-group snapshot listeners.
  • Android/iOS: maps the string option to the Firebase SDK’s server timestamp behavior and uses it when materializing snapshot data in result objects.

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/firestore/src/web.ts Forwards serverTimestamps into Firestore Web SDK data() calls for listener snapshots.
packages/firestore/src/definitions.ts Extends listener option types with serverTimestamps and documents its behavior.
packages/firestore/ios/Plugin/FirebaseFirestorePlugin.swift Reads serverTimestamps from plugin calls and passes it into listener option objects.
packages/firestore/ios/Plugin/FirebaseFirestoreHelper.swift Adds a helper to map string values to ServerTimestampBehavior.
packages/firestore/ios/Plugin/FirebaseFirestore.swift Threads server timestamp behavior into listener result creation.
packages/firestore/ios/Plugin/Classes/Results/GetDocumentResult.swift Uses data(with:) using the configured server timestamp behavior.
packages/firestore/ios/Plugin/Classes/Results/GetCollectionResult.swift Uses data(with:) using the configured server timestamp behavior.
packages/firestore/ios/Plugin/Classes/Results/GetCollectionGroupResult.swift Uses data(with:) using the configured server timestamp behavior.
packages/firestore/ios/Plugin/Classes/Options/AddDocumentSnapshotListenerOptions.swift Stores and exposes the server timestamp behavior string from JS.
packages/firestore/ios/Plugin/Classes/Options/AddCollectionSnapshotListenerOptions.swift Stores and exposes the server timestamp behavior string from JS.
packages/firestore/ios/Plugin/Classes/Options/AddCollectionGroupSnapshotListenerOptions.swift Stores and exposes the server timestamp behavior string from JS.
packages/firestore/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/firestore/FirebaseFirestorePlugin.java Reads serverTimestamps from plugin calls and passes it into listener option objects.
packages/firestore/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/firestore/FirebaseFirestoreHelper.java Adds a helper to map string values to DocumentSnapshot.ServerTimestampBehavior.
packages/firestore/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/firestore/FirebaseFirestore.java Threads server timestamp behavior into listener result creation.
packages/firestore/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/firestore/classes/results/GetDocumentResult.java Uses getData(serverTimestampBehavior) when building JS results.
packages/firestore/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/firestore/classes/results/GetCollectionResult.java Uses getData(serverTimestampBehavior) when building JS results.
packages/firestore/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/firestore/classes/options/AddDocumentSnapshotListenerOptions.java Adds storage/getter for the serverTimestamps string.
packages/firestore/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/firestore/classes/options/AddCollectionSnapshotListenerOptions.java Adds storage/getter for the serverTimestamps string.
packages/firestore/android/src/main/java/io/capawesome/capacitorjs/plugins/firebase/firestore/classes/options/AddCollectionGroupSnapshotListenerOptions.java Adds storage/getter for the serverTimestamps string.
.changeset/firestore-server-timestamps.md Declares a minor version bump for the new Firestore listener option.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/firestore/src/web.ts
Comment thread packages/firestore/src/web.ts
Comment thread packages/firestore/src/web.ts
- Normalize serverTimestamps on Web to match native fall-back behavior
- Remove unused Nullable import in GetDocumentResult.java
- Reformat definitions.ts with prettier 3.4.2 (pinned CI version)
@pkg-pr-new

pkg-pr-new Bot commented May 25, 2026

Copy link
Copy Markdown

Open in StackBlitz

@capacitor-firebase/analytics

npm i https://pkg.pr.new/capawesome-team/capacitor-firebase/@capacitor-firebase/analytics@993

@capacitor-firebase/app

npm i https://pkg.pr.new/capawesome-team/capacitor-firebase/@capacitor-firebase/app@993

@capacitor-firebase/app-check

npm i https://pkg.pr.new/capawesome-team/capacitor-firebase/@capacitor-firebase/app-check@993

@capacitor-firebase/authentication

npm i https://pkg.pr.new/capawesome-team/capacitor-firebase/@capacitor-firebase/authentication@993

@capacitor-firebase/crashlytics

npm i https://pkg.pr.new/capawesome-team/capacitor-firebase/@capacitor-firebase/crashlytics@993

@capacitor-firebase/firestore

npm i https://pkg.pr.new/capawesome-team/capacitor-firebase/@capacitor-firebase/firestore@993

@capacitor-firebase/functions

npm i https://pkg.pr.new/capawesome-team/capacitor-firebase/@capacitor-firebase/functions@993

@capacitor-firebase/messaging

npm i https://pkg.pr.new/capawesome-team/capacitor-firebase/@capacitor-firebase/messaging@993

@capacitor-firebase/performance

npm i https://pkg.pr.new/capawesome-team/capacitor-firebase/@capacitor-firebase/performance@993

@capacitor-firebase/remote-config

npm i https://pkg.pr.new/capawesome-team/capacitor-firebase/@capacitor-firebase/remote-config@993

@capacitor-firebase/storage

npm i https://pkg.pr.new/capawesome-team/capacitor-firebase/@capacitor-firebase/storage@993

commit: 8bee41b

@robingenz
robingenz merged commit 34dbed6 into main Jun 4, 2026
6 checks passed
@robingenz
robingenz deleted the feat/issue-985-server-timestamps branch June 4, 2026 05:47
@github-actions github-actions Bot mentioned this pull request Jun 4, 2026
riderx added a commit to Cap-go/capacitor-firebase that referenced this pull request Jun 26, 2026
* docs: update maintenance badge year [skip ci]

* feat(functions): add `timeout` option (capawesome-team#939)

* feat(capawesome-team#813): make functions timeout configurable

* chore(capawesome-team#813): format code

* Update packages/functions/src/definitions.ts

* docs: update `README.md`

---------

Co-authored-by: Robin Genz <mail@robingenz.dev>

* feat(functions): add `timeout` options

* docs: update banner

* fix(functions): Use emulator's regionOrCustomDomain option on native layers (capawesome-team#946)

* Add FAQ section to messaging package README (capawesome-team#951)

* Initial plan

* Add FAQ section to messaging package README

Add a new FAQ section below the API section in the messaging package's
README.md explaining the difference between the Firebase Cloud Messaging
plugin and the official Capacitor Push Notifications plugin.

Ref: capawesome-team#336

Co-authored-by: robingenz <13857929+robingenz@users.noreply.github.com>

* Apply suggestion from @robingenz

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: robingenz <13857929+robingenz@users.noreply.github.com>
Co-authored-by: Robin Genz <mail@robingenz.dev>

* fix: AGP 9.0 no longer supports `proguard-android.txt` (capawesome-team#952)

See ionic-team/capacitor#8315

* chore(release): publish (capawesome-team#940)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* chore: fix swiftlint scanning dependency files (capawesome-team#953)

* fix(authentication): reject calls when provider is not configured (capawesome-team#954)

* fix(authentication): reject calls when provider is not configured

Add null/nil checks for auth provider handlers so that calling a
sign-in or link method for a provider not listed in the Capacitor
config rejects with a clear error message instead of crashing (Android)
or silently hanging (iOS).

* refactor(authentication): remove @objc from throwing phone methods

These methods are only called from Swift, so @objc is unnecessary.

* docs: add changeset

* docs: update installation instructions to include AI-Assisted Setup

* docs: add newsletter subscription information to README files

* docs: format installation instructions for consistency across README files

* docs: update installation instructions to include link to Capawesome Skills

* feat(firestore): add support for Timestamp, GeoPoint, and FieldValue data types (capawesome-team#956)

* feat(firestore): add support for Timestamp, GeoPoint, and FieldValue data types

* fixes

* fix(firestore): Added missing data serialization in the FirebaseFirestoreClientestoreClient. (capawesome-team#957)

* fix(firestore): Added missing option serialization in the FirebaseFirestoreClient.

* added changeset.

* Delete .changeset/proud-donuts-mix.md

---------

Co-authored-by: Robin Genz <mail@robingenz.dev>

---------

Co-authored-by: Max Proshkin <20773529+mxs-dev@users.noreply.github.com>

* chore: add Firebase skills

* feat(analytics): add SPM trait to disable IDFA collection (capawesome-team#962)

* feat(firestore): add `enablePersistence` and `disablePersistence` methods (capawesome-team#961)

* feat(firestore): add enablePersistence and disablePersistence methods

* fix(firestore): fix Android build error in enablePersistence

* fix(firestore): use correct parameter types for deleteDocument and updateDocument

* fix(firestore): fix iOS build error with optional parameter type

* style: format

* docs

* docs: add missing changesets

* feat(firestore): add `databaseId` configuration option (capawesome-team#966)

* feat(firestore): add `databaseId` configuration option

* refactor(firestore): store Firestore instance in local variable

* fix(firestore): pass Firestore instance to query constraints on iOS

* fix(firestore): add `FirebaseFirestoreConfig.swift` to Xcode project

* feat(storage): add `downloadFile` method (capawesome-team#965)

* feat(storage): add `downloadFile` method

* fix(storage): add new Swift files to Xcode project

* style(storage): fix import ordering

* fix(storage): release call on error path and remove unused import

* feat(analytics): add `logTransaction` method (capawesome-team#967)

* feat(analytics): add `logTransaction` method

* style: format code

* fix(analytics): validate transaction ID before lookup

* docs(analytics): clarify transactionId property description

* feat(messaging): add `apnsTokenReceived` listener (capawesome-team#964)

* feat(messaging): add `getApnsToken` method (capawesome-team#602)

* fix(messaging): use uppercase hex for APNs token

* fix(messaging): reject when APNs token unavailable and document hex format

* refactor(messaging): extract APNs token error message to constant

* revert: remove getApnsToken method in favor of listener approach

* feat(messaging): add `apnsTokenReceived` listener (capawesome-team#602)

* style(messaging): format code

* style: format

* Revert "style: format"

This reverts commit 1e89351.

* fix(analytics): improve logTransaction iOS implementation (capawesome-team#968)

* docs: fix

* chore(release): publish (capawesome-team#955)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* docs(storage): update "Usage" section

* docs: add `--skill capacitor-plugins` flag to `npx skills add` command in all package READMEs (capawesome-team#969)

* Initial plan

* Replace npx skills add command with --skill capacitor-plugins flag in all package README.md files

Agent-Logs-Url: https://github.com/capawesome-team/capacitor-firebase/sessions/ac09d506-6bd7-4349-9e71-7598bd53d4bd

Co-authored-by: robingenz <13857929+robingenz@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: robingenz <13857929+robingenz@users.noreply.github.com>

* feat(remote-config): add setDefaults (capawesome-team#970)

* Update README.md

* chore(deps): npm audit fix

* feat(remote-config): add `getAll()` method (capawesome-team#977)

* ci(release): bump Node to 24 and drop npm self-update step

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(remote-config): expose value source on Web (capawesome-team#979)

* feat(remote-config): expose value source on Web for getBoolean, getNumber, getString, and getAll

* fix(remote-config): keep `source` optional to avoid breaking change

* fix(firestore): handle `DocumentReference` fields in web deserializer (capawesome-team#981)

Reading a document containing a `DocumentReference` field on web caused
`RangeError: Maximum call stack size exceeded` because `deserializeData`
had no branch for `DocumentReference` and fell through to the generic
object branch, which recursed into the cyclic `_firestore` getter.

Adds dedicated handling that produces a
`{ __type__: 'documentReference', id, path }` marker (mirroring
`Timestamp`/`GeoPoint`) and the symmetric `serializeMarker` case so a
deserialized reference can be written back.

Close capawesome-team#975

* docs(messaging): update link

* ci(lock): update GH action [skip ci]

* chore(tooling): add pre-commit prettier hook and consolidate prettier devDependency (capawesome-team#995)

* chore(tooling): add pre-commit prettier hook and consolidate prettier devDependency

* chore(tooling): bump CI Node to 24 and use --no-install for lint-staged hook

* docs: add Firebase SDK update versioning policy

* docs: document Swift Package Manager setup for plugins (capawesome-team#996)

* fix(firestore): generate unique listener IDs on web (capawesome-team#989)

* fix(firestore): generate unique listener IDs

Listener IDs were derived from `Date.now()`, so listeners created in the
same millisecond shared an ID and overwrote each other in the
unsubscribes map, leaving the earlier ones unable to be removed.

Replace the timestamp with a per-instance incrementing counter. The same
issue was present in the storage plugin (downloadFile/uploadFile
callback IDs) and is fixed here as well.

Closes capawesome-team#986

* chore: add changeset

* feat(firestore): support queries in getCountFromServer (capawesome-team#988)

* feat(firestore): support queries in getCountFromServer

* style: apply prettier formatting

* feat(firestore): support serverTimestamps option on snapshot listeners (capawesome-team#993)

* feat(firestore): add serverTimestamps option to snapshot listeners

Expose Firestore's pending server timestamp behavior ('estimate' |
'previous' | 'none') as an option on addDocumentSnapshotListener,
addCollectionSnapshotListener, and addCollectionGroupSnapshotListener
on Web, Android, and iOS. Defaults to 'none' (current behavior).

Closes capawesome-team#985

* fix(firestore): address PR review feedback

- Normalize serverTimestamps on Web to match native fall-back behavior
- Remove unused Nullable import in GetDocumentResult.java
- Reformat definitions.ts with prettier 3.4.2 (pinned CI version)

* feat(firestore): support DocumentReference, Bytes, and NaN/Infinity data types (capawesome-team#991)

* feat(firestore): add support for `DocumentReference`, `Bytes`, and `NaN`/`Infinity` data types

Closes capawesome-team#983

* fix(firestore): address review feedback and prettier formatting

- iOS: preserve all array elements in `createJSArrayFromArray` (use `map` with `NSNull()` fallback) to avoid silent data corruption.
- Web: chunk `Bytes.fromUint8Array` string build to avoid quadratic concat behavior.
- Extract shared `serializeSpecialNumber`/`deserializeSpecialNumber` helpers into `special-number.ts` so `web.ts` and `utils.ts` stay in sync.
- Apply pinned Prettier 3.4.2 formatting to `definitions.ts`.

* fix(firestore): thread Firestore instance through getCountFromServer

Resolve a semantic merge conflict: getCountFromServer (added on main)
called createQueryCompositeFilterConstraintFromJSObject with the old
signature, which this branch changed to require a Firestore instance for
DocumentReference support.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(firestore): avoid crash on malformed special number on Android

parseSpecialNumber fell back to Double.parseDouble, which throws on
malformed bridge data. Default to NaN instead, matching the graceful
fallback already used on iOS and Web.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(firestore): handle `limitToLast` query constraint on Android (capawesome-team#992)

* docs(firestore): add FieldValue operations

* feat(firestore): add `DocumentReference` class for reading and writing document reference fields (capawesome-team#999)

* feat(firestore): add `DocumentReference` class for reading and writing document reference fields

* fix(firestore): validate path in `DocumentReference.fromPath`

* chore(release): publish (capawesome-team#971)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* ci: increase iOS test job timeout for full monorepo sync

The upstream sync touches all packages, so SPM dependency installation
exceeds the 10-minute job cap.

Co-authored-by: Cursor <cursoragent@cursor.com>

* ci: extend iOS test timeout to 45 minutes for full sync builds

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore: fix formatting and relax swiftlint for upstream firestore growth

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: restore firestore getPluginVersion and sync native version strings

Keep Capgo fork API contract after upstream merge by restoring the
Firestore getPluginVersion bridge methods and aligning native plugin
version constants with package 8.3.0.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: address review feedback and repair iOS pluginVersion strings

- Fix malformed Swift pluginVersion literals that broke iOS CI
- Sync all package versions to 8.3.0 and remove upstream funding metadata
- Apply CodeRabbit fixes: timestamp nanoseconds, getCountFromServer
  serialization, weak analytics plugin ref, safe CallByUrl URL parsing,
  messaging README types reference, prettier bracketSameLine

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(app-check): align web getPluginVersion with package 8.3.0

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Robin Genz <mail@robingenz.dev>
Co-authored-by: Nico Genz <34741282+nicogenz@users.noreply.github.com>
Co-authored-by: Julian Scheuchenzuber <js@lvl51.de>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: robingenz <13857929+robingenz@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Max Proshkin <20773529+mxs-dev@users.noreply.github.com>
Co-authored-by: jacobg <jacobg@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(firestore): support different pending serverTimestamps behaviors

2 participants